{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/progress';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-aria/progress/package.json';
import Anatomy from './anatomy.svg';

---
category: Status
keywords: [progressbar, aria]
---

# useProgressBar

<PageDescription>{docs.exports.useProgressBar.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useProgressBar']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-1.2/#progressbar'}
  ]} />

## API

<FunctionAPI function={docs.exports.useProgressBar} links={docs.links} />

## Features

The [&lt;progress&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
HTML element can be used to build a progress bar, however it is
very difficult to style cross browser. `useProgressBar` helps achieve accessible
progress bars and spinners that can be styled as needed.

* Exposed to assistive technology as a progress bar via ARIA
* Labeling support for accessibility
* Internationalized number formatting as a percentage or value
* Determinate and indeterminate progress support

## Anatomy

<Anatomy />

Progress bars consist of a track element showing the full progress of an operation,
a fill element showing the current progress, a label, and an optional value label. The track and bar elements
represent the progress visually, while a wrapper element represents the progress to
assistive technology using the [progressbar](https://www.w3.org/TR/wai-aria-1.2/#progressbar)
ARIA role.

`useProgressBar` returns two sets of props that you should spread onto the appropriate element:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useProgressBar.return.id].properties} />
</TypeContext.Provider>

If there is no visual label, an `aria-label` or `aria-labelledby` prop must be passed instead
to identify the element to screen readers.

## Example

```tsx example export=true
import {useProgressBar} from '@react-aria/progress';

function ProgressBar(props) {
  let {
    label,
    showValueLabel = !!label,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let {
    progressBarProps,
    labelProps
  } = useProgressBar(props);

  // Calculate the width of the progress bar as a percentage
  let percentage = (value - minValue) / (maxValue - minValue);
  let barWidth = `${Math.round(percentage * 100)}%`;

  return (
    <div {...progressBarProps} style={{width: 200}}>
      <div style={{display: 'flex', justifyContent: 'space-between'}}>
        {label &&
          <span {...labelProps}>
            {label}
          </span>
        }
        {showValueLabel &&
          <span>
            {progressBarProps['aria-valuetext']}
          </span>
        }
      </div>
      <div style={{height: 10, background: 'lightgray'}}>
        <div style={{width: barWidth, height: 10, background: 'orange'}} />
      </div>
    </div>
  );
}

<ProgressBar label="Loading…" value={80} />
```

## Circular example

Progress bars may also be represented using a circular visualization rather than a line. This is often used to represent indeterminate
operations, but may also be used for determinate progress indicators when space is limited. The following example shows a progress bar
visualized as a circular spinner using SVG.

```tsx example export=true
function ProgressCircle(props) {
  let {isIndeterminate, value, minValue = 0, maxValue = 100} = props;
  let {progressBarProps} = useProgressBar(props);

  let center = 16;
  let strokeWidth = 4;
  let r = 16 - strokeWidth;
  let c = 2 * r * Math.PI;
  let percentage = isIndeterminate ? 0.25 : (value - minValue) / (maxValue - minValue);
  let offset = c - percentage * c;

  return (
    <svg
      {...progressBarProps}
      width={32}
      height={32}
      viewBox="0 0 32 32"
      fill="none"
      strokeWidth={strokeWidth}>
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="gray" />
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="orange"
        strokeDasharray={`${c} ${c}`}
        strokeDashoffset={offset}
        transform="rotate(-90 16 16)">
        {props.isIndeterminate &&
          <animateTransform
            attributeName="transform"
            type="rotate"
            begin="0s"
            dur="1s"
            from="0 16 16"
            to="360 16 16"
            repeatCount="indefinite" />
        }
      </circle>
    </svg>
  );
}

<ProgressCircle aria-label="Loading…" value={60} />
```

## Usage

The following examples show how to use the `ProgressBar` and `ProgressCircle` components created in the above example.

### Custom value scale

By default, the `value` prop represents the current percentage of progress, as the minimum and maximum values default to 0 and 100, respectively. Alternatively, a different scale can be used by setting the `minValue` and `maxValue` props.

```tsx example
<ProgressBar
  label="Loading…"
  minValue={50}
  maxValue={150}
  value={100} />
```

### Value formatting

Values are formatted as a percentage by default, but this can be modified by using the `formatOptions` prop to specify a different format.
`formatOptions` is compatible with the option parameter of [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) and is applied based on the current locale.

```tsx example
<ProgressBar
  label="Sending…"
  formatOptions={{style: 'currency', currency: 'JPY'}}
  value={60} />
```

### Custom value label

The `valueLabel` prop allows the formatted value to be replaced with a custom string.

```tsx example
<ProgressBar
  label="Feeding…"
  valueLabel="30 of 100 dogs"
  value={30} />
```

### Indeterminate

The `isIndeterminate` prop can be used to represent an indeterminate operation. The `ProgressCircle` component created [above](#circular-example)
implements an animation to indicate this visually.

```tsx example
<ProgressCircle
  aria-label="Loading…"
  isIndeterminate />
```

## Internationalization

### Value formatting

`useProgressBar` will handle localized formatting of the value label for accessibility
automatically. This is returned in the `aria-valuetext` prop in `progressBarProps`. You
can use this to create a visible label if needed and ensure that it is formatted correctly.
The number formatting can also be controlled using the `formatOptions` prop.

### RTL

In right-to-left languages, the progress bar should be mirrored. The label is right-aligned,
the value is left-aligned, and the fill progresses from right to left. Ensure that your CSS
accounts for this.
